<template>
  <transition
    :name="clsPrefix + 'zoom-in-top'"
    @after-leave="$emit('dodestroy')">
    <div
      v-show="visible"
      :class="[prefixCls, clsPrefix + 'picker-panel', clsPrefix + 'popper', popperClass]">
      <div :class="[prefixCls + '__content']">
        <div :class="[prefixCls + '__cell']">
          <div :class="[prefixCls + '__header']">{{ t(localePrefix + 'startTime') }}</div>
          <div
            :class="[prefixCls + '__body', clsPrefix + 'time-panel__content', { 'has-seconds': showSeconds, 'is-arrow': arrowControl }]">
            <time-spinner
              ref="minSpinner"
              :show-seconds="showSeconds"
              :am-pm-mode="amPmMode"
              @change="handleMinChange"
              :arrow-control="arrowControl"
              @select-range="setMinSelectionRange"
              :date="minDate">
            </time-spinner>
          </div>
        </div>
        <div :class="[prefixCls + '__cell']">
          <div :class="[prefixCls + '__header']">{{ t(localePrefix + 'endTime') }}</div>
          <div
            :class="[prefixCls + '__body', clsPrefix + 'time-panel__content', { 'has-seconds': showSeconds, 'is-arrow': arrowControl }]">
            <time-spinner
              ref="maxSpinner"
              :show-seconds="showSeconds"
              :am-pm-mode="amPmMode"
              @change="handleMaxChange"
              :arrow-control="arrowControl"
              @select-range="setMaxSelectionRange"
              :date="maxDate">
            </time-spinner>
          </div>
        </div>
      </div>
      <div :class="[clsPrefix + 'time-panel__footer']">
        <button
          type="button"
          :class="[clsPrefix + 'time-panel__btn', 'cancel']"
          @click="handleCancel()">{{ t(localePrefix + 'cancel') }}</button>
        <button
          type="button"
          :class="[clsPrefix + 'time-panel__btn', 'confirm']"
          @click="handleConfirm()"
          :disabled="btnDisabled">{{ t(localePrefix + 'confirm') }}</button>
      </div>
    </div>
  </transition>
</template>

<script type="text/babel">
import Config from '../../../config'
import {
  parseDate,
  limitTimeRange,
  modifyDate,
  clearMilliseconds,
  timeWithinRange
} from '../util'
import Locale from '../../../mixins/locale'
import TimeSpinner from '../basic/time-spinner'

const clsPrefix = Config.clsPrefix
const localePrefix = `${Config.localePrefix}.datepicker.`

const MIN_TIME = parseDate('00:00:00', 'HH:mm:ss')
const MAX_TIME = parseDate('23:59:59', 'HH:mm:ss')

const minTimeOfDay = function (date) {
  return modifyDate(MIN_TIME, date.getFullYear(), date.getMonth(), date.getDate())
}

const maxTimeOfDay = function (date) {
  return modifyDate(MAX_TIME, date.getFullYear(), date.getMonth(), date.getDate())
}

// increase time by amount of milliseconds, but within the range of day
const advanceTime = function (date, amount) {
  return new Date(Math.min(date.getTime() + amount, maxTimeOfDay(date).getTime()))
}

export default {
  mixins: [Locale],

  components: { TimeSpinner },

  computed: {
    showSeconds () {
      return (this.format || '').indexOf('ss') !== -1
    },

    offset () {
      return this.showSeconds ? 11 : 8
    },

    spinner () {
      return this.selectionRange[0] < this.offset ? this.$refs.minSpinner : this.$refs.maxSpinner
    },

    btnDisabled () {
      return this.minDate.getTime() > this.maxDate.getTime()
    },
    amPmMode () {
      if ((this.format || '').indexOf('A') !== -1) return 'A'
      if ((this.format || '').indexOf('a') !== -1) return 'a'
      return ''
    }
  },

  data () {
    return {
      clsPrefix: clsPrefix,
      prefixCls: `${clsPrefix}time-range-picker`,
      localePrefix: localePrefix,
      popperClass: '',
      minDate: new Date(),
      maxDate: new Date(),
      value: [],
      oldValue: [new Date(), new Date()],
      defaultValue: null,
      format: 'HH:mm:ss',
      visible: false,
      selectionRange: [0, 2],
      arrowControl: false
    }
  },

  watch: {
    value (value) {
      if (Array.isArray(value)) {
        this.minDate = new Date(value[0])
        this.maxDate = new Date(value[1])
      } else {
        if (Array.isArray(this.defaultValue)) {
          this.minDate = new Date(this.defaultValue[0])
          this.maxDate = new Date(this.defaultValue[1])
        } else if (this.defaultValue) {
          this.minDate = new Date(this.defaultValue)
          this.maxDate = advanceTime(new Date(this.defaultValue), 60 * 60 * 1000)
        } else {
          this.minDate = new Date()
          this.maxDate = advanceTime(new Date(), 60 * 60 * 1000)
        }
      }
    },

    visible (val) {
      if (val) {
        this.oldValue = this.value
        this.$nextTick(() => this.$refs.minSpinner.emitSelectRange('hours'))
      }
    }
  },

  methods: {
    handleClear () {
      this.$emit('pick', null)
    },

    handleCancel () {
      this.$emit('pick', this.oldValue)
    },

    handleMinChange (date) {
      this.minDate = clearMilliseconds(date)
      this.handleChange()
    },

    handleMaxChange (date) {
      this.maxDate = clearMilliseconds(date)
      this.handleChange()
    },

    handleChange () {
      if (this.isValidValue([this.minDate, this.maxDate])) {
        this.$refs.minSpinner.selectableRange = [[minTimeOfDay(this.minDate), this.maxDate]]
        this.$refs.maxSpinner.selectableRange = [[this.minDate, maxTimeOfDay(this.maxDate)]]
        this.$emit('pick', [this.minDate, this.maxDate], true)
      }
    },

    setMinSelectionRange (start, end) {
      this.$emit('select-range', start, end, 'min')
      this.selectionRange = [start, end]
    },

    setMaxSelectionRange (start, end) {
      this.$emit('select-range', start, end, 'max')
      this.selectionRange = [start + this.offset, end + this.offset]
    },

    handleConfirm (visible = false) {
      const minSelectableRange = this.$refs.minSpinner.selectableRange
      const maxSelectableRange = this.$refs.maxSpinner.selectableRange

      this.minDate = limitTimeRange(this.minDate, minSelectableRange, this.format)
      this.maxDate = limitTimeRange(this.maxDate, maxSelectableRange, this.format)

      this.$emit('pick', [this.minDate, this.maxDate], visible)
    },

    adjustSpinners () {
      this.$refs.minSpinner.adjustSpinners()
      this.$refs.maxSpinner.adjustSpinners()
    },

    changeSelectionRange (step) {
      const list = this.showSeconds ? [0, 3, 6, 11, 14, 17] : [0, 3, 8, 11]
      const mapping = ['hours', 'minutes'].concat(this.showSeconds ? ['seconds'] : [])
      const index = list.indexOf(this.selectionRange[0])
      const next = (index + step + list.length) % list.length
      const half = list.length / 2
      if (next < half) {
        this.$refs.minSpinner.emitSelectRange(mapping[next])
      } else {
        this.$refs.maxSpinner.emitSelectRange(mapping[next - half])
      }
    },

    isValidValue (date) {
      return Array.isArray(date) &&
        timeWithinRange(this.minDate, this.$refs.minSpinner.selectableRange) &&
        timeWithinRange(this.maxDate, this.$refs.maxSpinner.selectableRange)
    },

    handleKeydown (event) {
      const keyCode = event.keyCode
      const mapping = { 38: -1, 40: 1, 37: -1, 39: 1 }

      // Left or Right
      if (keyCode === 37 || keyCode === 39) {
        const step = mapping[keyCode]
        this.changeSelectionRange(step)
        event.preventDefault()
        return
      }

      // Up or Down
      if (keyCode === 38 || keyCode === 40) {
        const step = mapping[keyCode]
        this.spinner.scrollDown(step)
        event.preventDefault()
      }
    }
  }
}
</script>
